﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/15 14:47:39
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Gui;
using ImageK.Measure;
using ImageK.Process;

namespace ImageK
{
    /// <summary>
    /// This class consists of static methods and
    /// fields that implement ImageJ's Undo command.
    /// </summary>
    public class Undo
    {
        public const int NOTHING = 0;
        /** Undo using ImageProcessor.snapshot. */
        public const int FILTER = 1;
        /** Undo using an ImageProcessor copy. */
        public const int TYPE_CONVERSION = 2;
        public const int PASTE = 3;
        public const int COMPOUND_FILTER = 4;
        public const int COMPOUND_FILTER_DONE = 5;
        /** Undo using a single image, or composite color stack, copy (limited to 200MB). */
        public const int TRANSFORM = 6;
        public const int OVERLAY_ADDITION = 7;
        public const int ROI = 8;
        public const int MACRO = 9;

        private static int whatToUndo = NOTHING;
        private static int imageID;
        private static ImageProcessor ipCopy = null;
        private static ImagePlus impCopy;
        private static Calibration calCopy;
        private static Roi roiCopy;
        private static double displayRangeMin, displayRangeMax;
        private static LUT lutCopy;
        private static Overlay overlayCopy;

        public static void setup(int what, ImagePlus imp)
        {
            if (imp == null)
            {
                whatToUndo = NOTHING;
                reset();
                return;
            }
            if (IJ.debugMode) IJ.log("Undo.setup: " + what + " " + imp);
            if (what == FILTER && whatToUndo == COMPOUND_FILTER)
                return;
            if (what == COMPOUND_FILTER_DONE)
            {
                if (whatToUndo == COMPOUND_FILTER)
                    whatToUndo = what;
                return;
            }
            whatToUndo = what;
            imageID = imp.getID();
            if (what == TYPE_CONVERSION)
            {
                ipCopy = imp.getProcessor();
                calCopy = (Calibration)imp.getCalibration().clone();
            }
            else if (what == TRANSFORM)
            {
                //todo:
                // if ((!IJ.MacroRunning() || Prefs.supportMacroUndo) && (imp.getStackSize() == 1 || imp.getDisplayMode() == IJ.COMPOSITE) && imp.getSizeInBytes() < 209715200)
                //     impCopy = imp.duplicate();
                // else
                //     reset();
            }
            else if (what == MACRO)
            {
                ipCopy = imp.getProcessor().duplicate();
                calCopy = (Calibration)imp.getCalibration().clone();
                impCopy = null;
            }
            else if (what == COMPOUND_FILTER)
            {
                ImageProcessor ip = imp.getProcessor();
                if (ip != null)
                    ipCopy = ip.duplicate();
                else
                    ipCopy = null;
            }
            else if (what == OVERLAY_ADDITION)
            {
                impCopy = null;
                ipCopy = null;
            }
            else if (what == ROI)
            {
                //todo:
                // impCopy = null;
                // ipCopy = null;
                // Roi roi = imp.getRoi();
                // if (roi != null)
                // {
                //     roiCopy = (Roi)roi.clone();
                //     roiCopy.setImage(null);
                // }
                // else
                //     whatToUndo = NOTHING;
            }
            else
            {
                ipCopy = null;
                ImageProcessor ip = imp.getProcessor();
                lutCopy = (LUT)ip.getLut().Clone();
            }
        }

		public static void reset()
        {
			//if (IJ.debugMode) IJ.log("Undo.reset: "+ whatToUndo+" "+impCopy);
            if (whatToUndo == COMPOUND_FILTER || whatToUndo == OVERLAY_ADDITION)
                return;
            whatToUndo = NOTHING;
            imageID = 0;
            ipCopy = null;
            impCopy = null;
            calCopy = null;
            roiCopy = null;
            lutCopy = null;
            overlayCopy = null;
		}

		public static void undo()
		{
			ImagePlus imp = WindowManager.getCurrentImage();
			if (IJ.debugMode) IJ.log("Undo.undo: " + whatToUndo + " " + imp + "  " + impCopy);
			if (imp == null || imageID != imp.getID())
			{
				if (imp != null && !IJ.MacroRunning())
				{ // does image still have an undo buffer?
					ImageProcessor ip2 = imp.getProcessor();
					ip2.swapPixelArrays();
					imp.updateAndDraw();
				}
				else
					reset();
				return;
			}
			switch (whatToUndo)
			{
				case FILTER:
					// undoOverlay(imp);
					ImageProcessor ip = imp.getProcessor();
					if (ip != null)
					{
						if (!IJ.MacroRunning())
						{
							ip.swapPixelArrays();
							imp.updateAndDraw();
							return; // don't reset
						}
						else
						{
							ip.reset();
							imp.updateAndDraw();
						}
					}
					break;
				case TYPE_CONVERSION:
				case COMPOUND_FILTER:
				case COMPOUND_FILTER_DONE:
					if (ipCopy != null)
					{
                        if (whatToUndo == TYPE_CONVERSION && calCopy != null)
                        {
                            imp.setCalibration(calCopy);
                        }
						if (swapImages(new ImagePlus("", ipCopy), imp))
						{
							imp.updateAndDraw();
							return;
						}
						else
							imp.setProcessor(null, ipCopy);

                        if (whatToUndo == COMPOUND_FILTER_DONE)
                        {
                            undoOverlay(imp);
                        }
					}
					break;
				case TRANSFORM:
					if (impCopy != null)
						imp.setStack(impCopy.getStack());
					break;
				// case PASTE:
				// 	Roi roi = imp.getRoi();
				// 	if (roi != null)
				// 		roi.abortPaste();
				// 	break;
				// case ROI:
				// 	Roi roiCopy2 = roiCopy;
				// 	setup(ROI, imp); // setup redo
				// 	imp.setRoi(roiCopy2);
				// 	return; //don't reset
				// case MACRO:
				// 	if (ipCopy != null)
				// 	{
				// 		imp.setProcessor(ipCopy);
				// 		if (calCopy != null) imp.setCalibration(calCopy);
				// 	}
				// 	break;
				// case OVERLAY_ADDITION:
				// 	Overlay overlay = imp.getOverlay();
				// 	if (overlay == null)
				// 	{ IJ.beep(); return; }
				// 	int size = overlay.size();
				// 	if (size > 0)
				// 	{
				// 		overlay.remove(size - 1);
				// 		imp.draw();
				// 	}
				// 	else
				// 	{
				// 		IJ.beep();
				// 		return;
				// 	}
				// 	return; //don't reset
			}
			reset();
		}

        private static void undoOverlay(ImagePlus imp)
        {
            if (overlayCopy != null)
            {
                Overlay overlay = imp.getOverlay();
                if (overlay != null)
                {
                    imp.setOverlay(overlayCopy);
                    overlayCopy = overlay.duplicate();
                }
            }
        }

        static bool swapImages(ImagePlus imp1, ImagePlus imp2)
        {
            if (imp1.getWidth() != imp2.getWidth() || imp1.getHeight() != imp2.getHeight()
                                                   || imp1.getBitDepth() != imp2.getBitDepth() || IJ.MacroRunning())
                return false;
            ImageProcessor ip1 = imp1.getProcessor();
            ImageProcessor ip2 = imp2.getProcessor();
            double min1 = ip1.getMin();
            double max1 = ip1.getMax();
            double min2 = ip2.getMin();
            double max2 = ip2.getMax();
            ip2.setSnapshotPixels(ip1.getPixels());
            ip2.swapPixelArrays();
            ip1.setPixels(ip2.getSnapshotPixels());
            ip2.setSnapshotPixels(null);
            ip1.setMinAndMax(min2, max2);
            ip2.setMinAndMax(min1, max1);
            return true;
        }
    }
}
